package scenic;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;

import scenic.geom.Affine2;
import scenic.jni.Renderer;

/**
 * This is the base class for all scene graph nodes. Every node
 * with the exception of the root node has a single parent node. 
 * This means that the scene graph is tree-like in structure.
 */
public abstract class SceneNode {
	static final int safeMargin = 4;
	
	static final int DRAW_SIMPLE = 0;
	static final int DRAW_SURFACE = 1;
	static final int DRAW_SURFACE2X = 2;
	static final int DRAW_SURFACE4X = 3;
	
	private SceneParent parent;
	private ArrayList otherParents;
	private int visibilityCount;
	
	protected SceneNode() {
	}
	
	/**
	 * Gets the parent of this scene node.
	 */
/*	private SceneParent getParent() {
		return parent;
	} */ 

	void addParent(SceneParent newParent, boolean isVisible) {
		if(parent == null) {
			parent = newParent;
		} else {
			if(otherParents == null)
				otherParents = new ArrayList();
			otherParents.add(newParent);
		}
		if(isVisible)
			setParentVisibility(true);
	}
	
	void removeParent(SceneParent oldParent, boolean isVisible) {
		if(parent == oldParent) {
			if(otherParents != null && !otherParents.isEmpty()) {
				parent = (SceneParent)otherParents.remove(0);
			} else {
				parent = null;
			}
		} else {
			if(!otherParents.remove(oldParent))
				return;
		}
		if(isVisible)
			setParentVisibility(false);
	}
	
	void setParentVisibility(boolean isParentVisible) {
		if(isParentVisible) {
			visibilityCount++;
			if(visibilityCount == 1)
				setVisibility(true);
		} else {
			visibilityCount--;
			if(visibilityCount == 1)
				setVisibility(false);
		}
	}
	
	/**
	 * Removes this scene node from its parent.
	 */
/*	public void close() {
		if(parent != null)
			parent.remove(this);
	} */
	
	/**
	 * Gets the total transformation of this node. The total transformation
	 * is the cumulative transformation of this node and its ancestors.
	 */
	public Affine2 getTotalTransform() {
		if (parent != null)
			return parent.getTotalTransform();
		else
			return Affine2.identity();
	}
	
	abstract void draw(DrawContext context, Affine2 transform, Rectangle visibleArea);
	
	void prepareDraw(DrawContext context, Affine2 transform) {
	}
	
	abstract Rectangle getBounds(Affine2 transform);
	
	void setVisibility(boolean state) {
	}
	
	protected boolean getVisibility() {
		return visibilityCount > 0;
	}
	
	void changed(Rectangle area) {
		if(parent != null)
			parent.childChanged(area);
		if(otherParents != null) {
			synchronized(otherParents) {
				Iterator itr = otherParents.iterator();
			
				while(itr.hasNext()) {
					((SceneParent)itr.next()).childChanged(area);
				}
			}
		}
	}
	
	void changed() {
		changed(null);
	}
	
	int getDrawType(Affine2 transform) {
		return Shape.DRAW_SIMPLE;
	}
	
	static int getSurfaceType(int drawType) {
		if(drawType == DRAW_SURFACE2X)
			return Renderer.SURFACE_TYPE_ALPHA2X;
		if(drawType == DRAW_SURFACE4X)
			return Renderer.SURFACE_TYPE_ALPHA4X;
		if(drawType == DRAW_SURFACE)
			return Renderer.SURFACE_TYPE_ALPHA;
		return Renderer.SURFACE_TYPE_COLOR;
	}
	
	static Rectangle addSafeMargin(Rectangle bounds) {
		return new Rectangle(bounds.x - safeMargin, bounds.y - safeMargin, 
				bounds.width + safeMargin * 2, bounds.height + safeMargin * 2);
	}
}
